/*****************************************************************************
 *   Copyright 2005 Tim A Wang
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 ****************************************************************************/
package com.jb.web.context;

import com.jb.database.metamodel.Database;
import com.jb.faces.FactoryFinder;
import com.jb.faces.application.Application;
import com.jb.faces.application.ApplicationFactory;
import com.jb.faces.application.FacesMessage;
import com.jb.faces.component.UIComponent;
import com.jb.faces.component.UIViewRoot;
import com.jb.faces.context.ExternalContext;
import com.jb.faces.context.FacesContext;
import com.jb.faces.context.ResponseStream;
import com.jb.faces.context.ResponseWriter;
import com.jb.faces.event.PhaseId;
import com.jb.faces.render.RenderKit;
import com.jb.framework.PersistentContext;
import com.jb.framework.PersistentManager;
import com.jb.framework.Scope;
import com.jb.resource.ResourceManager;
import com.jb.web.application.ApplicationException;
import com.jb.web.application.ApplicationImpl;
import com.jb.web.application.NWConstants;
import com.jb.web.application.WebService;
import com.jb.web.application.configuration.Config;
import com.jb.web.application.configuration.ManagedBeanDef;
import com.jb.web.bean.BaseComponent;
import com.jb.web.bean.Page;
import com.jb.web.bean.UIComponentImpl;
import com.jb.web.model.css.CssStyle;
import com.jb.web.renderer.RenderKitFactoryImpl;
import com.jb.web.session.Session;
import com.jb.web.session.SessionManager;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletConfig;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * WebContext holds the state for a lifecycle of request and response. It is
 * passed to most event handlers. Its main purpose is to allow user code to
 * share a context in the same life cycle of processing a request. The
 * followings are the main functions:
 *
 * <ul>
 * <li>
 * Add error messages
 * </li>
 * <li>
 * Using scoped variables
 * </li>
 * <li>
 * Access to higher scope object, such as Session, and Application
 * </li>
 * <li>
 * Used by other services to process localized
 * </li>
 * <li>
 * provide localized message (resources)
 * </li>
 * </ul>
 */
public class WebContext extends FacesContext implements PersistentContext , Scope{
    private Log log = LogFactory.getLog(getClass().getName());

    public boolean isRenderResponse = false;
    public boolean isResponseComplete = false;
    private WebService service;//custom service serves the requst
    /**
     * Adds a key value pair to current url, if it is already exist, value will be overridden.
     * This can be called by event handler to manipulate request url before page is
     * rendered back
     */
    private HashMap mQuery = new HashMap();

    //jb tool bar page, displayed at the top of the page
    private Page toolBar;
    private ResponseStream mStream;

    //
    private ResponseWriter writer;
    private String onLoad = null;

    //View root
    private UIViewRoot viewRoot;

    //Reference of current application
    private ApplicationImpl application;

    // private Hashtable messageStack = new Hashtable();
    //component that the request is made from
    private BaseComponent requestComponent;

    //component tree where the request comes from
    private BaseComponent requestTree = null;

    //respond tree to be written out
    private BaseComponent respondTree = null;

    //
    //error stack can be added while processing request,
    //if this is not empty, redirect is ignored
    //values put in only get onces, and it will be removed.
    //
    private Hashtable errorStack = new Hashtable();

    //cache to hold context parameters
    private Hashtable parameters = new Hashtable();

    //tempary holder for the servlet request
    private HttpServletRequest request;
    private HttpServletResponse response;

    //phase id as indicate of life cycle of procssing a request
    private PhaseId phase;

    //Server side representation of a client session
    private Session session;

    //web application url 
    private String applicationUrl;
    private String flashMessage;

    //page name, xsp name for the request
    private String pageName;

    //scripts to be added just before end body tag
    private String postBodyScript = "";

    //redirect page, if not null
    private String redirectPage;

    //request url
    private String requestURL;

    //web top directory, starting from webapps in tomcat
    private String webTop;
    private boolean deployed;

    private StringBuffer ajaxResponse = new StringBuffer();

    public WebContext() {
        try
        {
            application = (ApplicationImpl)((ApplicationFactory)FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY)).getApplication();
        }catch(Exception e) {
          //  e.printStackTrace();
        }
    }
    
    public WebContext(ApplicationImpl apps) {
        application = apps;
    }

    public WebContext(ServletConfig ctx) {
        webTop = ctx.getServletContext().getRealPath("/");
    }

    /**
     * Constructs an instance from HTTP Request and Response Object.
     *
     * @param request
     * @param response
     */
    public WebContext(HttpServletRequest request, 
                      HttpServletResponse response) {
        init(request, response);
    }

    /**
     * Returns error stack as a string.
     *
     * @return
     */
    public String getAllErrors() {
        Iterator iter = errorStack.keySet().iterator();
        StringBuffer errorStackMsg = new StringBuffer();

        while (iter.hasNext()) {
            Object key = iter.next();
            FacesMessage facesMessage = (FacesMessage)errorStack.get(key);

            if (Config.getInstance().getDebugLevel() < 
                10) {
                errorStackMsg.append(facesMessage.getDetail() + "\n");
            } else {
                errorStackMsg.append(facesMessage.getDetail() + "\n");
            }
        }

        errorStack.clear();

        return errorStackMsg.toString();
    }

    /**
     * Assign the application to current context
     *
     * @param application
     */
    public void setApplication(ApplicationImpl application) {
        this.application = application;
    }

    /**
     * Return application object.
     *
     * @return application.
     */
    public Application getApplication() {
        return application;
    }

    public String getApplicationName() {
        if (session == null)
            return Config.getInstance().getDefaultApplication();
        else
            return session.getApplicationName();
    }

    /**
     * Returns application top.
     *
     * @return application top.
     */
    public String getApplicationTop() {
        return application.getApplicationTop(this);
    }

    /**
     * Returns url of application top. For example, http://127.0.0.1:8998/jb
     *
     * @return
     */
    public String getAppsURL() {
        return applicationUrl;
    }

    /**
     * Return iterator of messages ??
     *
     * @return iterator of client ids.
     */
    public Iterator getClientIdsWithMessages() {
        return null;
    }

    /**
     * Returns list of cookies currently visible by the browser session
     *
     * @return
     */
    public HashMap getCookies() {
        HashMap cookies = new HashMap();
        Cookie[] cs = request.getCookies();

        if (cs != null) {
            for (int i = 0; i < cs.length; i++) {
                cookies.put(cs[i].getName(), cs[i]);
            }
        }
        return cookies;
    }

    public Database getDatabase() {
        if (this.session == null)
            return null;
        if (session.getAttribute("com.jb.myDB_metadata") == null) {
            session.addAttribute("com.jb.myDB_metadata", new Database());
        }
        return (Database)session.getAttribute("com.jb.myDB_metadata");
    }

    /**
     * Sets deployed flag.
     *
     * @param deployed DOCUMENT ME!
     */
    public void setDeployed(boolean deployed) {
        this.deployed = deployed;
    }

    /**
     * Returns deployed flag.
     *
     * @return if application is deployed or not.
     */
    public boolean isDeployed() {
        return deployed;
    }

    /**
     * Returns true if there are some errors at the time call.
     *
     * @return true if there is an error.
     */
    public boolean isError() {
        return !errorStack.isEmpty();
    }

    /**
     * Get error messages in string reported on a particular
     * component.
     * @param component the component error messages are reported
     * against.
     * @return message in localized string.
     */
    public String getErrorMessage(Object component) {
        FacesMessage msg = null;
        Object key = null;

        if (component == null) {
            key = "Unknown Source";
        } else if (component instanceof BaseComponent) {
            key = ((BaseComponent)component).getClientId();
        } else {
            key = component.toString();
        }

        if (key != null) {
            msg = (FacesMessage)errorStack.get(key);

            if (msg != null) {
                errorStack.remove(key);
            }
        }

        return (msg == null) ? null : msg.getDetail();
    }


    public void writeAJaxResponse(String msg) {
        ajaxResponse.append(msg);
    }

    public String getAJaxResponse() {
        if (ajaxResponse.length() == 0)
            return null;
        else
            return ajaxResponse.toString();
    }

    /**
     * Returns accumulated error messages.
     *
     * @return error messages map.
     */
    public Map getErrorMessages() {
        return errorStack;
    }

    /**
     * Return error map.
     *
     * @return error messages.
     */
    public Map getErrors() {
        return errorStack;
    }

    /**
     * Returns external context injected into this context.
     *
     * @return External context.
     */
    public ExternalContext getExternalContext() {
        return null;
    }

    public void setFlashMessage(String pMessage) {
        flashMessage = pMessage;
    }

    public String getFlashMessage() {
        return flashMessage;
    }

    /**
     * Returns the max error severity this context will sport.
     *
     * @return Severity
     */
    public FacesMessage.Severity getMaximumSeverity() {
        return FacesMessage.SEVERITY_FATAL;
    }

    public Iterator getMessages(String key) {
        return errorStack.values().iterator();
    }

    public Iterator getMessages() {
        return errorStack.values().iterator();
    }

    /**
     * Gets onload function.
     *
     * @return page onload function.
     */
    public String getOnLoad() {
        return onLoad;
    }

    public void setPageName(String pageName) {
        this.pageName = pageName;
    }

    /**
     * Returns current page name.
     *
     * @return page name.
     */
    public String getPageName() {
        return pageName;
    }
    /**
     * Returns respond page. This value can change during the process. 
     * @return respond page instance.
     */
    public Page getPage() {
        if(this.respondTree instanceof Page)
            return (Page) respondTree;
        else
            return null;
    }
    

    /**
     * Returns a name scoped parameter value.
     *
     * @param key
     *
     * @return
     */
    public Object getParameter(Object key) {
        return parameters.get(key);
    }

    /**
     * Return all scoped variables as a map.
     *
     * @return
     */
    public Map getParameters() {
        return parameters;
    }


    /**
     * Returns personal persistent manager
     *
     * @return
     */
    
    public synchronized PersistentManager getPersistentManager() {
        return session.getPersistentManager();
    }    

    public synchronized PersistentManager getPersonalPM() {
        return session.getPersonalPM();
    }  
    /**
     * Sets jsf phase.
     *
     * @param phase jsf phase.
     */
    public void setPhase(PhaseId phase) {
        this.phase = phase;
    }

    /**
     * Returns jsf phase.
     *
     * @return phaseId jsf phase id.
     */
    public PhaseId getPhase() {
        return phase;
    }

    private List droppables = new ArrayList();

    /**
     * Adds an element to droppable list
     * @param droppable the client id of the component that might accept drag and drop.
     */
    public void addDroppable(String droppable) {
        droppables.add(droppable);
    }

    /**
     * Returns the droppable list.
     * @return list of droppables;
     */
    public List getDroppables() {
        return droppables;
    }

    /**
     * Returns java scripts needed just after body tag
     *
     * @return
     */
    public String getPostBodyScript() {
        return postBodyScript;
    }

    /**
     * Sets redirect page.
     *
     * @param pageName
     */
    public void setRedirectPage(String pageName) {
        redirectPage = pageName;
    }

    /**
     * Returns redirect page
     *
     * @return
     */
    public String getRedirectPage() {
        return redirectPage;
    }

    /**
     * Returns renderring kit. Not implemented
     *
     * @return DOCUMENT ME!
     */
    public RenderKit getRenderKit() {
        return null;
    }

    /**
     * Return true if render response has been requested. This is used to transfer
     * the process into renderring response by the lifecycle controller.
     *
     * @return ture if needs to goning to renderring response phase.
     */
    public boolean getRenderResponse() {
        return isRenderResponse;
    }

    /**
     * Binds context to servlet request.
     *
     * @param request servlet request.
     */
    public void setRequest(HttpServletRequest request) {
        this.request = request;
    }

    /**
     * Returns bound servlet request.
     *
     * @return request servlet request.
     */
    public HttpServletRequest getRequest() {
        return request;
    }

    /**
     * Sets request component tree.
     *
     * @param requestComponent
     */
    public void setRequestComponent(BaseComponent requestComponent) {
        this.requestComponent = requestComponent;
    }

    /**
     * Returns request component tree.
     *
     * @return request component tree.
     */
    public BaseComponent getRequestComponent() {
        return requestComponent;
    }

    /**
     * Returns root component of this request.
     *
     * @return
     */
    public BaseComponent getRequestTree() {
        return requestTree;
    }

    /**
     * Returns request URL.
     * @return request URL.
     */
    public String getRequestURL() {
        return requestURL;
    }

    /**
     * Returns an i18n message for the given key. Applications can
     * use this API programmatically get i18n message to display. It returns
     * key passed in if not found.
     *
     * @return message
     */
    public String getResource(String key) {
        String res = getResources().getProperty(key);

        if (res != null) {
            return res;
        } else {
            return key;
        }
    }

    /**
     * Return resource with bind variables.
     *
     * @param key key to the resource.
     * @param tokens binding vlaues
     *
     * @return resource value.
     */
    public String getResource(String key, String[] tokens) {
        return ResourceManager.getInstance().getMessage(this, key, tokens);
    }

    /**
     * Returns resouces loaded in this context container. Applications can
     * use this API programmatically get i18n message to display.
     *
     * @return list of properties
     */
    public Properties getResources() {
        return ResourceManager.getInstance().getProperties(this);
    }

    /**
     * Sets respond tree by name
     *
     * @param pageName
     */
    public void setRespondPage(String pageName) {
        if (pageName == null) {
            return;
        }

        if (!pageName.equals(respondTree.getId())) {
            respondTree = session.getPage(this, pageName, false);
        }
    }

    /**
     * Sets repsonse root component
     *
     * @param respondTree component.
     */
    public void setRespondTree(UIComponentImpl respondTree) {
        this.respondTree = respondTree;
    }

    /**
     * Returns repsonse root component
     *
     * @return root component.
     */
    public BaseComponent getRespondTree() {
        return respondTree;
    }

    /**
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public HttpServletResponse getResponse() {
        return response;
    }

    /**
     * Return true if response has been done.
     *
     * @return true if response has been complete.
     */
    public boolean getResponseComplete() {
        return isResponseComplete;
    }

    /**
     * Sets response stream. Not implemented.
     *
     * @param pStream reponse stream to which response to the user will be stream
     * to.
     */
    public void setResponseStream(ResponseStream pStream) {
        mStream = pStream;
    }

    /**
     * Returns response stream.
     *
     * @return Resonse stream.
     */
    public ResponseStream getResponseStream() {
        return mStream;
    }

    /**
     * Sets response write. This writer is responsible for write back response to
     * the user. UTF-8 should be used for encoding.
     *
     * @param writer
     */
    public void setResponseWriter(ResponseWriter writer) {
        this.writer = writer;
    }

    /**
     * Returns the response writer.
     *
     * @return the response writer.
     */
    public ResponseWriter getResponseWriter() {
        if (writer == null) {
            try {
                RenderKitFactoryImpl renderFactory = 
                    (RenderKitFactoryImpl)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);

                writer = renderFactory.getRenderKit(this, application.getDefaultRenderKitId())
                            .createResponseWriter(response.getWriter(),  "text/html", "UTF-8");
            } catch (IOException e) {
                if (log.isErrorEnabled())
                    log.error(this, e);
                //
            }
        }
        return writer;
    }
    /**
     * Returns true if session is not in design mode.
     *
     * @return
     */
    public boolean isRunTime() {
        return (session == null) ? false : session.isRunTime();
    }
    /**
     * Returns true if the request component is on the page to render.
     * @return true if request and response tree are the same.
     */
    public boolean isSamePage() {
        return getRequestTree() == getRespondTree();
    }
    /**
     * Returns if the object is currently selected in the session. This method is used by render the
     * component.
     *
     * @param obj
     *
     * @return
     */
    public boolean isSelected(UIComponentImpl obj) {
        if ((obj == null) || session == null || 
            session.getSelected() == null) {
            return false;
        }

        return obj.isDesignable() && 
            obj.getId().equals(session.getSelected().getId());
    }
    /**
     * Assign a session object to this context.
     *
     * @param newSession
     */
    public void setSession(Session newSession) {
        session = newSession;
    }
    /**
     * Returns session object associated with this context.
     *
     * @return the session this context is attached.
     */
    public Session getSession() {
        return session;
    }
    /**
     * Returns an UI component that is in the request tree. If not, then
     * find it in the respond tree. Null is returned if not found in both
     * trees. In many cases, request tree is the same as the respond tree.
     * @param clientID the client id to the UI component.
     * @return the UI component.
     */
    public UIComponent getRequestComponent(String clientID) {
        if (clientID == null || getRequestTree() == null) {
            if (log.isErrorEnabled()) {
                log.error("Failed to get respond component by id " + clientID);
            }
            return null;
        }
        UIComponent comp = getRequestTree().getDescendantByClientId(clientID);
        if (comp == null) {
            comp = this.getRespondTree().getDescendantByClientId(clientID);
        }
        return comp;
    }

    /**
     * Returns an UI component that is inthe respond tree. Null if not found. 
     * @param clientID the client id to the UI component
     * @return
     */
    public UIComponent getRepondComponent(String clientID) {
        if (clientID == null || getRespondTree() == null) {
            if (log.isErrorEnabled()) {
                log.error("Failed to get respond component by id " + clientID);
            }
            return null;
        }
        return getRespondTree().getDescendantByClientId(clientID);
    }

    /**
     * Get a list of CssStyle components on the request page.
     * @return list of CssStyle components.
     */
    public List getStyleClasses() {
        if (respondTree instanceof Page) {
            List list = respondTree.getChildrenByType(CssStyle.class);

            for (int i = list.size() - 1; i >= 0; i--) {
                CssStyle style = (CssStyle)list.get(i);

                if (!style.isPersonalizable()) {
                    list.remove(style);
                }
            }
            return list;
        } else {
            return Collections.EMPTY_LIST;
        }
    }

    /**
     * Returns the persistent manager for templates
     *
     * @return the persistent manager for templates. Not used.
     */
    public PersistentManager getTemplatePM() {
        return application.getTemplatePM();
    }

    /**
     * Assigns the toolbar menu to current context
     *
     * @param toolBar
     */
    public void setToolBar(Page toolBar) {
        this.toolBar = toolBar;
    }

    /**
     * Returns page definition for tool.
     *
     * @return the toolbar page.
     */
    public Page getToolBar() {
        if ((toolBar == null) && !isDeployed()) {
            toolBar = getSession().getToolBar(this);
        }

        return toolBar;
    }

    /**
     * Returns Html Uri that caused this life cycle.
     *
     * @return the current URI.
     */
    public String getUri() {
        return (getRequest() == null) ? "" : getRequest().getRequestURI();
    }

    /**
     * Attachs view root to this context.
     *
     * @param root the view root to be attached.
     */
    public void setViewRoot(UIViewRoot root) {
        viewRoot = root;
    }

    /**
     * Implmented per JSF spec. Returns the view root attached to this
     * context.
     *
     * @return the view root attached to this component.
     */
    public UIViewRoot getViewRoot() {
        return viewRoot;
    }

    /**
     *  Sets webtop for this context.
     * @param webTop
     */
    public void setWebTop(String webTop) {
        this.webTop = webTop;
    }

    /**
     * Returns absolute location of this web application.
     *
     * @return
     */
    public String getWebTop() {
        return webTop;
    }

    /**
     * Adds a named cookie to the client browser. The max age
     * is default to 2000 hours.
     *
     * @param name the name of the cookie.
     * @param value the value of the cookie. 
     */
    public void addCookie(String name, String value) {
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge(3600 * 2000);
        response.addCookie(cookie);
    }

    /**
     * Adds an error message for a given component
     *
     * @param pComponent where error occured
     * @param pMessage message
     */
    public void addErrorMessage(Object pComponent, String pMessage) {
        if ((pMessage == null) || (pMessage.length() == 0)) {
            return; //no needs
        }

        if (pComponent == null) {
            addMessage("Unknown Source", 
                       new FacesMessage(FacesMessage.SEVERITY_ERROR, 
                                        "JB Error", pMessage));
        } else if (pComponent instanceof BaseComponent) {
            addMessage(((BaseComponent)pComponent).getClientId(), 
                       new FacesMessage(FacesMessage.SEVERITY_ERROR, 
                                        "Component Error", pMessage));
        } else {
            if (pComponent.toString() != null) {
                addMessage(pComponent.toString(), 
                           new FacesMessage(FacesMessage.SEVERITY_ERROR, 
                                            "Object Error", pMessage));
            } else {
                addMessage(pComponent.getClass().getName(), 
                           new FacesMessage(FacesMessage.SEVERITY_ERROR, 
                                            "Object Error", pMessage));
            }
        }
    }

    /**
     * Adds a message to this context.
     *
     * @param key the hash key for the message.
     * @param msg the FacesMessage to be added.
     */
    public void addMessage(String key, FacesMessage msg) {
        //Map errors = errorStack.get(key);
        errorStack.put(key, msg);
    }

    /**
     * Adds client side statement to page onload function. Statement must be leagal
     * javascript statement. Sequence of statements is enforced based on time they are
     * added.
     *
     * <p>Applicaiton developers can use this in event handlers, but they need to
     * make sure the function is corret.
     *
     * <p>Component writers should not use this function.
     *
     * @param onLoadStmt DOCUMENT ME!
     */
    public void addOnLoadStmt(String onLoadStmt) {
        if (this.onLoad == null) {
            this.onLoad = "\n" + onLoadStmt;
        } else {
            this.onLoad = this.onLoad + "\n" + onLoadStmt;
        }
    }

    /**
     * Adds a scoped parameter.
     *
     * @param key
     * @param value
     */
    public void addParameter(Object key, Object value) {
        if ((key == null) || (value == null)) {
            return;
        }
        parameters.put(key, value);
    }
    
    List includedScripts = new ArrayList();
    
    public void includeScript(String name) {
        if(!includedScripts.contains(name)) {
            includedScripts.add(name);
        }
    }
    public List getIncludedScript() {
        return includedScripts;
    }
    /**
     * Add java scripts that will be inserted into return page after body tag
     *
     * @param pValue
     */
    public void addPostBodyScript(String pValue) {
        postBodyScript = postBodyScript + "\n" + pValue;
    }

    public void addQueryToRequestURL(String key, String value) {
        mQuery.put(key, value);
    }
    /**
     * Cleans up context.
     */
    public void cleanUp() {
        if (session != null) {//TODO, we can not clear context for a session. as one session is used by more than on context
          //  session.setContext(null);
          //  this.session = null;
        }
    }

    /**
     * Formulate a leagal URL string that can be used by renderer when rendering. Logic if contains
     * protocal portion in the string, then it is an absolute url, return as it is if starts with
     * javascript, then it is a javascript call, return as it is
     *
     * @param url
     * @param pComponent DOCUMENT ME!
     *
     * @return
     */
    public String formURL(String url, UIComponentImpl pComponent) {
        if ((url == null) || (url.length() == 0)) {
            return url;
        }

        if (url.startsWith("::")) //leave as it is
        {
            return url.substring(2);
        }

        //return as it is since this will be a direct to external page
        if (url.indexOf("://") != -1) {
            return url;
        }

        //return as it is since this is a javascript call
        if (url.startsWith("javascript")) {
            return url;
        }

        //add souce object name if necessary, otherwise, return as it is
        StringBuffer regionNameBuffer = new StringBuffer(url);

        if (url.indexOf("?") != -1) {
            if (url.indexOf(NWConstants.NW_EVENT_SOURCE) == 
                -1) {
                regionNameBuffer.append("&").append(NWConstants.NW_EVENT_SOURCE).append("=").append(pComponent.getQName());
            }

            return regionNameBuffer.toString();
        }

        //append .xsp if not exist
        if (url.indexOf(".xsp") == -1) {
            regionNameBuffer.append(".xsp");
        }

        //add source 
        regionNameBuffer.append("?").append(NWConstants.NW_EVENT_SOURCE).append("=").append(pComponent.getQName());

        String query = request.getParameter("pageMode");

        if ((query != null) && (query.length() != 0)) {
            regionNameBuffer.append("&pageMode=").append(query);
        }

        return regionNameBuffer.toString();
    }

    /**
     * Test whether a component has any errors so far in the lifecycle. If 
     * component is null, returns true if there is any error at all, regardless
     * component. If component is a BaseComponent, it's client Id is used 
     * as key to test errors. If not, the toString is used
     * as hashkey to test errors.
     * @param component the component to be tested.
     * @return true if the test is positive.
     */
    public boolean hasError(Object component) {
        if (component == null) {
            return errorStack.size() > 0;
        }

        FacesMessage msg = null;
        Object key = null;

        if (component instanceof BaseComponent) {
            key = ((BaseComponent)component).getClientId();
        } else {
            key = component.toString();
        }

        if (key != null) {
            msg = (FacesMessage)errorStack.get(key);
        }

        return msg != null;
    }

    /**
     * Initializes the context separating the creation and initialization
     * of a web context.
     * @throws com.jb.web.application.ApplicationException
     */
    private void initialize() throws ApplicationException {
        if (log.isDebugEnabled()) {
            log.debug("Initialize " + this.getClass().getName());
        }

        String fName = null;
        String uri = request.getRequestURI();
        droppables.clear();
        if (uri.indexOf(NWConstants.NW_EXT) != -1) {
            fName = uri.substring(uri.lastIndexOf("/") + 1);
        }

        if ((fName == null) || (fName.length() == 0)) {
            fName = Config.getInstance().getDefaultPage(); //default region    
        }

        if ((fName != null) && (fName.indexOf(".xsp") > -1)) {
            fName = fName.substring(0, fName.indexOf("." + NWConstants.NW_PAGE_EXT));
        }

        if (log.isDebugEnabled()) {
            log.debug("Make context for " + fName);
        }

        //session info, create one if does not exist
        setPageName(fName);

        //this will either get existing one back or create a new session
        Session session = SessionManager.findOrCreate(request);
        if(session==null) {
            throw new RuntimeException("Fatal error, can not create session manager.");
        }
        session.bindContext(this);

        requestURL = uri;
        Page mainPage = session.getPage(this, fName, false);
        if (mainPage == null) {
            if (log.isDebugEnabled()) {
                log.debug("Page " + fName + "not found, go to home" + 
                          Config.getInstance().getDefaultPage());
            }

            fName = Config.getInstance().getDefaultPage();
            addParameter("pageName", fName);
            mainPage = session.getPage(this, fName, false);

            if (session.getUser().getAccess() < 1) {
                mainPage = session.getPage(this, "designer", false);
            }

            if (mainPage == null) {
                addErrorMessage(this, "Can not find default page " + fName);
                renderResponse();
            }
        }

        if (mainPage == null) {
            throw new ApplicationException(this, "Failed to find page " + fName);
        }

        //find the event component
        //this returns component id where request was initiated
        //build request tree
        String requestSource = request.getParameter(NWConstants.NW_EVENT_SOURCE);
        UIComponentImpl requestComponent = mainPage;

        if (requestSource != null) {
            String requestPage = 
                requestSource.substring(0, requestSource.indexOf(":"));

            if (!requestPage.equals(mainPage.getId())) {
                requestComponent = session.getPage(this, requestPage, false);
            }

            if (requestComponent != null) {
                setRequestComponent(requestComponent.getComponentByQName(requestSource));
            }
        }

        setRequestTree(requestComponent);
        setRespondTree(mainPage);

        Iterator iter=Config.getInstance().getBeans().iterator();
        while(iter.hasNext()) {
            ManagedBeanDef mbd= (ManagedBeanDef) iter.next();
            if("request".equals(mbd.getScope())) {
                Object obj = mbd.createInstance();
                if(obj!=null)
                    this.addParameter(mbd.getName(), obj);
            }
        }        

        if (log.isDebugEnabled()) {
            log.debug("Context initialized for " + fName);
        }
    }

    /**
     * Release resources used by this context.
     */
    public void release() {
    }

    /**
     * Removes the named cookie for current session.
     * @param name the name of the cookie to be removed.
     */
    public void removeCookie(String name) {
        //by setting it to an empty string
        Cookie cookie = new Cookie(name, "");
        cookie.setMaxAge(3600 * 2000);
        response.addCookie(cookie);
    }

    /**
     * Flags the request has been process. Jumps to render response.
     */
    public void renderResponse() {
        isRenderResponse = true;
    }

    /**
     * Flags the request has been rendered.
     */
    public void responseComplete() {
        isResponseComplete = true;
    }

    /**
     * Sets request root component.
     *
     * @param requestTree
     */
    private void setRequestTree(Object requestTree) {
        this.requestTree = (UIComponentImpl)requestTree;
    }

    private void init(HttpServletRequest pRequest, 
                      HttpServletResponse pResponse) {
        request = pRequest;
        response = pResponse;
                
        parameters = new Hashtable();
        webTop = pRequest.getSession().getServletContext().getRealPath("/");
        Enumeration enu = request.getParameterNames();
        while (enu.hasMoreElements()) {
            String name = (String)enu.nextElement();
            parameters.put(name, request.getParameter(name));
        }
        applicationUrl = pRequest.getRequestURL().toString();
        application = (ApplicationImpl)((ApplicationFactory)FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY)).getApplication();
        initialize();
    }
    /**
     * Sets service for this context.
     * @param param new service.
     */
    public void setService(WebService param) {
        this.service = param;
    }
    /**
     * Returns service for this context.
     * @return the serivce.
     */
    
    public WebService getService() {
        return service;
    }
}
